//
// Created by 高曦祥 on 2023/11/29.
//

#include <iostream>
#include "util/logger.h"
#include "util/base_types.h"
#include <thread>
#include <condition_variable>
#include <mutex>
#include <gtest/gtest.h>
#include <vector>
#include <atomic>
#include "util/rw_lock.h"

using namespace TinyDB;

t_bool finish = false;
t_bool w_flag = false;
t_bool w_flag1 = false; 
std::condition_variable cv;
std::mutex mtx;
RWLock rw_lock;

void read_func() {
	rw_lock.RLock();
	std::unique_lock<std::mutex> lock(mtx);
	cv.wait(lock, [&] { return finish; });
	rw_lock.UnRLock();
}

void write_func() {
	rw_lock.WLock();
	w_flag = true;
	rw_lock.UnWLock();
}

void write_func1() {
	rw_lock.WLock();
	w_flag1 = true;
	std::unique_lock<std::mutex> lock(mtx);
	cv.wait(lock, [&] { return finish; });
	w_flag1 = false;
	rw_lock.UnWLock();
}

// 多个读锁可以共存
TEST(LOCK_TEST, TEST1) {
	std::vector<std::thread> th_vec;
	t_uint32 th_num = 100;
	finish = false;
	for (t_uint32 i = 0; i < th_num; i++) {
		th_vec.push_back(std::thread(read_func));
	}
	std::this_thread::sleep_for(std::chrono::milliseconds(100));
	t_uint32 rn;
	rn = rw_lock.GetReadCnt();
	EXPECT_EQ(th_num, rn);

	{
		std::lock_guard<std::mutex> guard(mtx);
		finish = true;
		cv.notify_all();
	}

	for (std::thread& th : th_vec) {
		th.join();
	}

	rn = rw_lock.GetReadCnt();
	EXPECT_EQ(0, rn);
}

// 读锁与写锁是互斥的
TEST(LOCK_TEST, TEST2) {
	// 读锁先占有
	finish = false;
	w_flag = false;
	t_uint32 rd_th_num = 100;
	std::vector<std::thread> th_vec;
	for (t_uint32 i = 0; i < rd_th_num; i++) {
		th_vec.push_back(std::thread(read_func));
	}
	std::this_thread::sleep_for(std::chrono::milliseconds(100));

	// 此时写锁无法申请到
	th_vec.push_back(std::thread(write_func));
	t_bool flag = rw_lock.HasWriter();
	ASSERT_EQ(false, flag);
	ASSERT_EQ(rd_th_num, rw_lock.GetReadCnt());

	// 释放读锁 写锁才可申请到
	{
		std::lock_guard<std::mutex> guard(mtx);
		finish = true;
		cv.notify_all();
	}

	std::this_thread::sleep_for(std::chrono::milliseconds(100));

	ASSERT_EQ(true, w_flag);

	for(std::thread& th : th_vec) {
		th.join();
	}

}

// 写锁与写锁之间是互斥的
TEST(LOCK_TEST, TEST3) {
	finish = false;
	w_flag = false;
	w_flag1 = false;

	std::thread th1(write_func1);
	std::this_thread::sleep_for(std::chrono::milliseconds(100));
	ASSERT_EQ(true, rw_lock.HasWriter());
	ASSERT_EQ(true, w_flag1);

	std::thread th2(write_func);
	std::this_thread::sleep_for(std::chrono::milliseconds(100));
	ASSERT_EQ(false, w_flag);

	{
		std::lock_guard<std::mutex> guard(mtx);
		finish = true;
		cv.notify_all();
	}
	
	std::this_thread::sleep_for(std::chrono::milliseconds(100));
	ASSERT_EQ(true, w_flag);
	ASSERT_EQ(false, w_flag1);
	th1.join();
	th2.join();
}
